package com.cryptoneedle.center.source.service.crud;

import cn.hutool.v7.core.tree.MapTree;
import cn.hutool.v7.core.tree.TreeNode;
import cn.hutool.v7.core.tree.TreeNodeConfig;
import cn.hutool.v7.core.tree.TreeUtil;
import com.cryptoneedle.center.source.entity.constraint.*;
import com.cryptoneedle.center.source.entity.enhance.Dimension;
import com.cryptoneedle.center.source.entity.metadata.*;
import com.cryptoneedle.center.source.enums.TreeNodeType;
import com.cryptoneedle.center.source.key.constraint.ConstraintCheckKey;
import com.cryptoneedle.center.source.key.constraint.ConstraintColumnKey;
import com.cryptoneedle.center.source.key.constraint.IndexKey;
import com.cryptoneedle.center.source.key.enhance.DimensionKey;
import com.cryptoneedle.center.source.key.metadata.ColumnKey;
import com.cryptoneedle.center.source.key.metadata.DatabaseKey;
import com.cryptoneedle.center.source.key.metadata.ServerKey;
import com.cryptoneedle.center.source.key.metadata.TableKey;
import com.cryptoneedle.center.source.repository.constraint.*;
import com.cryptoneedle.center.source.repository.enhance.DimensionRepository;
import com.cryptoneedle.center.source.repository.metadata.*;
import com.cryptoneedle.center.source.vo.SourceTreeDto;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>description:  </p>
 *
 * @author CryptoNeedle
 * @date 2025-10-23
 */
@Slf4j
@Service
@Transactional
public class FoundDataService {

    @Autowired
    private FoundDataService select;
    @Autowired
    private AddDataService add;
    @Autowired
    private PatchDataService patch;
    @Autowired
    private SaveDataService save;
    @Autowired
    private DeleteDataService delete;

    @Resource
    private ServerRepository serverRepository;
    @Resource
    private CatalogRepository catalogRepository;
    @Resource
    private DatabaseRepository databaseRepository;
    @Resource
    private TableRepository tableRepository;
    @Resource
    private ColumnRepository columnRepository;

    @Resource
    private ConstraintCheckRepository constraintCheckRepository;
    @Resource
    private ConstraintForeignRepository constraintForeignRepository;
    @Resource
    private ConstraintPrimaryRepository constraintPrimaryRepository;
    @Resource
    private ConstraintUniqueRepository constraintUniqueRepository;
    @Resource
    private IndexUniqueRepository indexUniqueRepository;

    @Resource
    private DimensionRepository dimensionRepository;

    /**
     * Server
     */

    public Server server(ServerKey key) {
        return serverRepository.findById(key).orElse(null);
    }

    public Server server(String host, Integer port) {
        return select.server(new ServerKey(host, port));
    }

    public Server server(String catalogName) {
        Catalog catalog = select.catalog(catalogName);
        return select.server(catalog);
    }

    public Server server(Catalog catalog) {
        return select.server(catalog.serverKey());
    }

    public List<Server> servers() {
        return serverRepository.servers();
    }

    /**
     * Catalog
     */

    public Catalog catalog(String key) {
        return catalogRepository.findById(key).orElse(null);
    }

    public Catalog catalogByDorisCatalogName(String dorisCatalogName) {
        return catalogRepository.catalogByDorisCatalogName(dorisCatalogName);
    }

    public List<Catalog> catalogs() {
        return catalogRepository.catalogs();
    }

    public List<Catalog> catalogsEnabled() {
        return catalogRepository.catalogsEnabled();
    }

    public List<Catalog> catalogs(Server server) {
        return catalogRepository.catalogsByServer(server.getHost(), server.getPort());
    }

    /**
     * Database
     */

    public Database database(DatabaseKey key) {
        return databaseRepository.findById(key).orElse(null);
    }

    public Database database(String catalogName, String databaseName) {
        return select.database(new DatabaseKey(catalogName, databaseName));
    }

    public List<Database> databases() {
        return databaseRepository.databases();
    }

    public List<Database> databases(Catalog catalog) {
        return select.databases(catalog.getCatalogName());
    }

    public List<Database> databases(String catalogName) {
        return databaseRepository.databases(catalogName);
    }

    public List<Database> databasesEnabled() {
        return databaseRepository.databasesEnabled();
    }

    public List<Database> databasesEnabled(Catalog catalog) {
        return select.databasesEnabled(catalog.getCatalogName());
    }

    public List<Database> databasesEnabled(String catalogName) {
        return databaseRepository.databasesEnabled(catalogName);
    }

    /**
     * Table
     */

    public Table table(TableKey key) {
        return tableRepository.findById(key).orElse(null);
    }

    public Table table(String catalogName, String databaseName, String tableName) {
        return select.table(new TableKey(catalogName, databaseName, tableName));
    }

    public List<Table> tables() {
        return tableRepository.tables();
    }

    public List<Table> tables(Catalog catalog) {
        return select.tables(catalog.getCatalogName());
    }

    public List<Table> tables(String catalogName) {
        return tableRepository.tables(catalogName);
    }

    public List<Table> tables(Database database) {
        return select.tables(database.getCatalogName(), database.getDatabaseName());
    }

    public List<Table> tables(String catalogName, String databaseName) {
        return tableRepository.tables(catalogName, databaseName);
    }

    public List<Table> tablesEnabled() {
        return tableRepository.tablesEnabled();
    }

    public List<Table> tablesEnabled(Catalog catalog) {
        return select.tablesEnabled(catalog.getCatalogName());
    }

    public List<Table> tablesEnabled(String catalogName) {
        return tableRepository.tablesEnabled(catalogName);
    }

    public List<Table> tablesEnabled(Database database) {
        return select.tablesEnabled(database.getCatalogName(), database.getDatabaseName());
    }

    public List<Table> tablesEnabled(String catalogName, String databaseName) {
        return tableRepository.tablesEnabled(catalogName, databaseName);
    }

    /**
     * Column
     */

    public Column column(ColumnKey key) {
        return columnRepository.findById(key).orElse(null);
    }

    public Column column(String catalogName, String databaseName, String tableName, String columnName) {
        return select.column(new ColumnKey(catalogName, databaseName, tableName, columnName));
    }

    public List<Column> columns(Catalog catalog) {
        return select.columns(catalog.getCatalogName());
    }

    public List<Column> columns(String catalogName) {
        return columnRepository.columns(catalogName);
    }

    public List<Column> columns(Database database) {
        return select.columns(database.getCatalogName(), database.getDatabaseName());
    }

    public List<Column> columns(String catalogName, String databaseName) {
        return columnRepository.columns(catalogName, databaseName);
    }

    public List<Column> columns(Table table) {
        return select.columns(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<Column> columns(String catalogName, String databaseName, String tableName) {
        return columnRepository.columns(catalogName, databaseName, tableName);
    }

    public List<Column> columnsWithDimension(Table table) {
        return columnRepository.columnsWithDimension(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<Column> columnsWithoutDimension(Table table) {
        return columnRepository.columnsWithoutDimension(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    /**
     * ConstraintCheck
     */

    public ConstraintCheck constraintCheck(ConstraintCheckKey key) {
        return constraintCheckRepository.findById(key).orElse(null);
    }

    public ConstraintCheck constraintCheck(String catalogName, String databaseName, String tableName, String constraintName) {
        return select.constraintCheck(new ConstraintCheckKey(catalogName, databaseName, tableName, constraintName));
    }

    public List<ConstraintCheck> constraintChecks(Catalog catalog) {
        return select.constraintChecks(catalog.getCatalogName());
    }

    public List<ConstraintCheck> constraintChecks(String catalogName) {
        return constraintCheckRepository.constraintChecks(catalogName);
    }

    public List<ConstraintCheck> constraintChecks(Database database) {
        return select.constraintChecks(database.getCatalogName(), database.getDatabaseName());
    }

    public List<ConstraintCheck> constraintChecks(String catalogName, String databaseName) {
        return constraintCheckRepository.constraintChecks(catalogName, databaseName);
    }

    public List<ConstraintCheck> constraintChecks(Table table) {
        return select.constraintChecks(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<ConstraintCheck> constraintChecks(String catalogName, String databaseName, String tableName) {
        return constraintCheckRepository.constraintChecks(catalogName, databaseName, tableName);
    }

    /**
     * ConstraintForeign
     */

    public ConstraintForeign constraintForeign(ConstraintColumnKey key) {
        return constraintForeignRepository.findById(key).orElse(null);
    }

    public ConstraintForeign constraintForeign(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintForeign(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<ConstraintForeign> constraintForeigns(Catalog catalog) {
        return select.constraintForeigns(catalog.getCatalogName());
    }

    public List<ConstraintForeign> constraintForeigns(String catalogName) {
        return constraintForeignRepository.constraintForeigns(catalogName);
    }

    public List<ConstraintForeign> constraintForeigns(Database database) {
        return select.constraintForeigns(database.getCatalogName(), database.getDatabaseName());
    }

    public List<ConstraintForeign> constraintForeigns(String catalogName, String databaseName) {
        return constraintForeignRepository.constraintForeigns(catalogName, databaseName);
    }

    public List<ConstraintForeign> constraintForeigns(Table table) {
        return select.constraintForeigns(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<ConstraintForeign> constraintForeigns(String catalogName, String databaseName, String tableName) {
        return constraintForeignRepository.constraintForeigns(catalogName, databaseName, tableName);
    }

    public List<ConstraintForeign> constraintForeigns(Column column) {
        return select.constraintForeigns(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<ConstraintForeign> constraintForeigns(String catalogName, String databaseName, String tableName, String columnName) {
        return constraintForeignRepository.constraintForeigns(catalogName, databaseName, tableName, columnName);
    }

    /**
     * ConstraintPrimary
     */

    public ConstraintPrimary constraintPrimary(ConstraintColumnKey key) {
        return constraintPrimaryRepository.findById(key).orElse(null);
    }

    public ConstraintPrimary constraintPrimary(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintPrimary(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<ConstraintPrimary> constraintPrimaries(Catalog catalog) {
        return select.constraintPrimaries(catalog.getCatalogName());
    }

    public List<ConstraintPrimary> constraintPrimaries(String catalogName) {
        return constraintPrimaryRepository.constraintPrimaries(catalogName);
    }

    public List<ConstraintPrimary> constraintPrimaries(Database database) {
        return select.constraintPrimaries(database.getCatalogName(), database.getDatabaseName());
    }

    public List<ConstraintPrimary> constraintPrimaries(String catalogName, String databaseName) {
        return constraintPrimaryRepository.constraintPrimaries(catalogName, databaseName);
    }

    public List<ConstraintPrimary> constraintPrimaries(Table table) {
        return select.constraintPrimaries(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<ConstraintPrimary> constraintPrimaries(String catalogName, String databaseName, String tableName) {
        return constraintPrimaryRepository.constraintPrimaries(catalogName, databaseName, tableName);
    }

    public List<ConstraintPrimary> constraintPrimaries(Column column) {
        return select.constraintPrimaries(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<ConstraintPrimary> constraintPrimaries(String catalogName, String databaseName, String tableName, String columnName) {
        return constraintPrimaryRepository.constraintPrimaries(catalogName, databaseName, tableName, columnName);
    }

    /**
     * ConstraintUnique
     */

    public ConstraintUnique constraintUnique(ConstraintColumnKey key) {
        return constraintUniqueRepository.findById(key).orElse(null);
    }

    public ConstraintUnique constraintUnique(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintUnique(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<ConstraintUnique> constraintUniques(Catalog catalog) {
        return select.constraintUniques(catalog.getCatalogName());
    }

    public List<ConstraintUnique> constraintUniques(String catalogName) {
        return constraintUniqueRepository.constraintUniques(catalogName);
    }

    public List<ConstraintUnique> constraintUniques(Database database) {
        return select.constraintUniques(database.getCatalogName(), database.getDatabaseName());
    }

    public List<ConstraintUnique> constraintUniques(String catalogName, String databaseName) {
        return constraintUniqueRepository.constraintUniques(catalogName, databaseName);
    }

    public List<ConstraintUnique> constraintUniques(Table table) {
        return select.constraintUniques(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<ConstraintUnique> constraintUniques(String catalogName, String databaseName, String tableName) {
        return constraintUniqueRepository.constraintUniques(catalogName, databaseName, tableName);
    }

    public List<ConstraintUnique> constraintUniques(Column column) {
        return select.constraintUniques(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<ConstraintUnique> constraintUniques(String catalogName, String databaseName, String tableName, String columnName) {
        return constraintUniqueRepository.constraintUniques(catalogName, databaseName, tableName, columnName);
    }

    /**
     * IndexUnique
     */

    public IndexUnique indexUnique(IndexKey key) {
        return indexUniqueRepository.findById(key).orElse(null);
    }

    public IndexUnique indexUnique(String catalogName, String databaseName, String tableName, String indexName, String columnName) {
        return select.indexUnique(new IndexKey(catalogName, databaseName, tableName, indexName, columnName));
    }

    public List<IndexUnique> indexUniques(Catalog catalog) {
        return select.indexUniques(catalog.getCatalogName());
    }

    public List<IndexUnique> indexUniques(String catalogName) {
        return indexUniqueRepository.indexUniques(catalogName);
    }

    public List<IndexUnique> indexUniques(Database database) {
        return select.indexUniques(database.getCatalogName(), database.getDatabaseName());
    }

    public List<IndexUnique> indexUniques(String catalogName, String databaseName) {
        return indexUniqueRepository.indexUniques(catalogName, databaseName);
    }

    public List<IndexUnique> indexUniques(Table table) {
        return select.indexUniques(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<IndexUnique> indexUniques(String catalogName, String databaseName, String tableName) {
        return indexUniqueRepository.indexUniques(catalogName, databaseName, tableName);
    }

    public List<IndexUnique> indexUniques(Column column) {
        return select.indexUniques(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<IndexUnique> indexUniques(String catalogName, String databaseName, String tableName, String columnName) {
        return indexUniqueRepository.indexUniques(catalogName, databaseName, tableName, columnName);
    }

    /**
     * Dimension
     */

    public Dimension dimension(DimensionKey key) {
        return dimensionRepository.findById(key).orElse(null);
    }

    public Dimension dimension(String catalogName, String databaseName, String tableName, String dimensionName, String columnName) {
        return select.dimension(new DimensionKey(catalogName, databaseName, tableName, dimensionName, columnName));
    }

    public List<Dimension> dimensions(Catalog catalog) {
        return select.dimensions(catalog.getCatalogName());
    }

    public List<Dimension> dimensions(String catalogName) {
        return dimensionRepository.dimensions(catalogName);
    }

    public List<Dimension> dimensions(Database database) {
        return select.dimensions(database.getCatalogName(), database.getDatabaseName());
    }

    public List<Dimension> dimensions(String catalogName, String databaseName) {
        return dimensionRepository.dimensions(catalogName, databaseName);
    }

    public List<Dimension> dimensions(Table table) {
        return select.dimensions(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<Dimension> dimensions(String catalogName, String databaseName, String tableName) {
        return dimensionRepository.dimensions(catalogName, databaseName, tableName);
    }

    public List<Dimension> dimensions(Column column) {
        return select.dimensions(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<Dimension> dimensions(String catalogName, String databaseName, String tableName, String columnName) {
        return dimensionRepository.dimensions(catalogName, databaseName, tableName, columnName);
    }

    public List<Dimension> dimensionsEnabled(Table table) {
        return dimensionRepository.dimensionsEnabled(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<Dimension> dimensionsEnabled(String catalogName, String databaseName, String tableName) {
        return dimensionRepository.dimensionsEnabled(catalogName, databaseName, tableName);
    }

    /**
     * Tree
     */

    public List<MapTree<Object>> tree(Boolean displayServer, Boolean displayTable) {
        List<SourceTreeDto> sourceTreeDtos = new ArrayList<>();

        // 服务器
        if (displayServer) {
            sourceTreeDtos.addAll(select
                    .servers()
                    .stream()
                    .map(server -> new SourceTreeDto()
                            .setId(server.inetAddress())
                            .setParentId(null)
                            .setName(server.inetAddress())
                            .setNodeType(TreeNodeType.SERVER.name())
                            .setDatabaseType(null)
                            .setEnabled(true)
                            .setActiveDimensionName(null))
                    .toList());
        }

        // Catalog
        sourceTreeDtos.addAll(select
                .catalogs()
                .stream()
                .map(catalog -> new SourceTreeDto()
                        .setId(catalog.getCatalogName())
                        .setParentId(displayServer ? catalog.inetAddress() : null)
                        .setName(catalog.getCatalogName())
                        .setNodeType(TreeNodeType.CATALOG.name())
                        .setDatabaseType(catalog.getDatabaseType())
                        .setEnabled(catalog.getEnabled())
                        .setActiveDimensionName(null))
                .toList());

        // 数据库
        sourceTreeDtos.addAll(select
                .databasesEnabled()
                .stream()
                .map(database -> new SourceTreeDto()
                        .setId(database.getDatabaseName())
                        .setParentId(database.getCatalogName())
                        .setName(database.getDatabaseName())
                        .setNodeType(TreeNodeType.DATABASE.name())
                        .setDatabaseType(null)
                        .setEnabled(database.getEnabled())
                        .setActiveDimensionName(null))
                .toList());

        // 表
        if (displayTable) {
            sourceTreeDtos.addAll(select
                    .tablesEnabled()
                    .stream()
                    .map(table -> new SourceTreeDto()
                            .setId(table.getTableName())
                            .setParentId(table.getDatabaseName())
                            .setName(table.getTableName())
                            .setNodeType(TreeNodeType.TABLE.name())
                            .setDatabaseType(null)
                            .setEnabled(table.getEnabled())
                            .setActiveDimensionName(table.getActiveDimensionName()))
                    .toList());
        }

        AtomicInteger sort = new AtomicInteger();
        List<TreeNode<String>> treeNodeList = sourceTreeDtos
                .stream()
                .map(treeDto -> new TreeNode<String>()
                        .setId(treeDto.getId())
                        .setParentId(treeDto.getParentId())
                        .setName(treeDto.getName())
                        .setWeight(sort.getAndIncrement())
                        .setExtra(new HashMap<>() {{
                            put("nodeType", treeDto.getNodeType());
                            put("databaseType", treeDto.getDatabaseType());
                        }}))
                .toList();

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        treeNodeConfig.setNameKey("name");
        treeNodeConfig.setDeep(4);

        return TreeUtil.build(treeNodeList, null, treeNodeConfig, (object, treeNode) -> {
            treeNode.setParentId(object.getParentId());
            treeNode.setId(object.getId());
            treeNode.setName(object.getName());
            treeNode.setWeight(object.getWeight());
            treeNode.putExtra("nodeType", object.getExtra().get("nodeType"));
            treeNode.putExtra("databaseType", object.getExtra().get("databaseType"));
            treeNode.putExtra("enabled", object.getExtra().get("enabled"));
            treeNode.putExtra("activeDimensionName", object.getExtra().get("activeDimensionName"));
        });
    }
}